package cn.hejiang.mamis.core.magic;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Component;
import org.ssssssss.magicapi.core.annotation.MagicModule;
import org.ssssssss.script.annotation.Comment;
import org.ssssssss.script.annotation.Function;

import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;

@Component
@Slf4j
@MagicModule("webhook")
public class MagicWebhook {
	@Autowired
	CacheManager cacheManager;

	@Function
	@Comment("发送钉钉自定义机器人消息。相关信息详见钉钉文档: https://open.dingtalk.com/document/robots/custom-robot-access")
	public DingDing ding(
			@Comment("Webhook 地址。例如: https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxx") String url,
			@Comment("加签一栏下面显示的SEC开头的字符串") String secret) {
		return new DingDing(url, secret, cacheManager.getCache("DING"));
	}

	public static class DingDing {
		private String url;
		private String secret;
		private Cache cache;

		public DingDing(String url, String secret, Cache cache) {
			this.url = url;
			this.secret = secret;
			this.cache = cache;
		}

		@Comment("发送钉钉消息。相同消息在 N 秒内不重复发送(使用 MD5 检测重复发送)")
		public Object send(@Comment("消息体对象") final Object msg, int dontSendAgainInSeconds) {
			String id = DigestUtil.md5Hex(JSONUtil.toJsonStr(msg));
			return send(msg, dontSendAgainInSeconds, id);
		}

		@Comment("发送钉钉消息。相同消息在 N 秒内不重复发送, 使用 id 检测重复发送")
		public Object send(@Comment("消息体对象") final Object msg, int dontSendAgainInSeconds, String id) {
			String key = new StringBuilder(url).append("\n").append(secret).append("\n").append(id).toString();
			Ehcache ec = (Ehcache) cache.getNativeCache();
			Element el = ec.get(key);
			if (el != null) {
				return el.getObjectValue();
			}

			Object value = null;
			ec.acquireWriteLockOnKey(key);
			try {
				el = ec.get(key); // one more attempt with the write lock
				if (el != null) {
					return el.getObjectValue();
				} else {
					value = send(msg);
					ec.put(new Element(key, value, 0, dontSendAgainInSeconds));
				}
			} finally {
				ec.releaseWriteLockOnKey(key);
			}

			return value;
		}

		@Comment("发送钉钉消息")
		public Object send(@Comment("消息体对象") final Object msg) {
			Long timestamp = System.currentTimeMillis();
			try {
				String sign = signSecret(timestamp);
				String signedUrl = url + "&timestamp=" + timestamp + "&sign=" + sign;

				HttpResponse response = HttpUtil.createPost(signedUrl).body(JSONUtil.toJsonStr(msg), "application/json")
						.execute();
				String body = response.body();

				log.info("发送钉钉消息返回: {}", body);
				return JSONUtil.toBean(body, HashMap.class);
			} catch (Exception e) {
				log.error(e.getLocalizedMessage(), e);
			}

			return null;
		}

		private String signSecret(Long timestamp)
				throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
			String stringToSign = timestamp + "\n" + secret;
			Mac mac = Mac.getInstance("HmacSHA256");
			mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
			byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
			String sign = URLEncoder.encode(new String(Base64.encodeBase64(signData)), "UTF-8");
			return sign;
		}
	}
}
